package cn.cocho.dborm.schema;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import android.content.res.AssetManager;
import cn.cocho.dborm.domain.ColumnBean;
import cn.cocho.dborm.domain.TableBean;
import cn.cocho.dborm.util.LoggerUtilsDborm;
import cn.cocho.dborm.util.DbormContexts;
import cn.cocho.dborm.util.StringUtilsDborm;

/**
 * 操作Schema
 * 
 * @author dborm@cocho
 * @time 2013-5-23下午3:25:42
 */
public class SchemaInit {

	private String schemaPath;

	/**
	 * 初始化表结构
	 * 
	 * @return 配置信息集合
	 * @throws Exception
	 * @author dborm@cocho
	 * @time 2013-5-23下午3:26:20
	 */
	public Hashtable<String, TableBean> initSchema() throws Exception {
		Hashtable<String, TableBean> tables = new Hashtable<String, TableBean>();
        Set<String> schemaFiles = getSchemaFiles();
		for (String schemaFile : schemaFiles) {
			String schemaFilePath = schemaPath + File.separator + schemaFile;
			tables.putAll(getSchemaByFile(schemaFilePath));
		}
		return tables;
	}

	private Set<String> getSchemaFiles() throws Exception {
		AssetManager assetManager = DbormContexts.context.getResources().getAssets();
		Set<String> schemaFiles = new HashSet<String>();
		try {
            String[] fileNames = assetManager.list(DbormContexts.schemaPath);
            for (String fileName : fileNames) {
                if (fileName.endsWith(".xml")) {//只加载xml文件
                    schemaFiles.add(fileName);
                }
            }
        } catch (IOException e) {
			throw new IllegalArgumentException("无法读取schema文件:" + DbormContexts.schemaPath + "");
		}
		return schemaFiles;
	}

	private Map<String, TableBean> getSchemaByFile(String schemaFilePath) {
		Map<String, TableBean> tables = new HashMap<String, TableBean>();
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		Document document = null;
		try {
			InputStream inputStream = DbormContexts.context.getResources().getAssets().open(schemaFilePath);
			DocumentBuilder builder = factory.newDocumentBuilder();
			document = builder.parse(inputStream);
		} catch (ParserConfigurationException e) {
			LoggerUtilsDborm.error(SchemaInit.class.getName(), e);
		} catch (SAXException e) {
			LoggerUtilsDborm.error(SchemaInit.class.getName(), e);
		} catch (IOException e) {
			LoggerUtilsDborm.error(SchemaInit.class.getName(), e);
		}
		if (document != null) {
			Element root = document.getDocumentElement();// 获得根元素
			NodeList methodList = root.getElementsByTagName(SchemaConstants.TABLE);// 获得名称为method的元素集合
			for (int i = 0; i < methodList.getLength(); i++) {// 遍历节点 DocumentBuilder builder=null;  DocumentBuilder builder=null; 
				Element table = (Element) methodList.item(i);
				String classPath = getStringMustAttributeValue(table, SchemaConstants.TABLE_CLASS_PATH);
                if(isValibleClassPath(classPath)){
                    String name = getStringAttributeValue(table, SchemaConstants.TABLE_NAME);
                    if (name == null) {
                        String className = classPath.substring(classPath.lastIndexOf("\\."));
                        StringUtilsDborm.humpToUnderlineName(className);
                    }
                    TableBean tableDomain = new TableBean();
                    tableDomain.setClassPath(classPath);
                    tableDomain.setTableName(name);
                    tableDomain.setColumns(getColumnDomains(tableDomain, table));
                    tableDomain.setRelation(getRelations(tableDomain, table));
                    tables.put(classPath, tableDomain);
                }
			}
		}
		return tables;
	}

	/**
	 * 获得列属性集合
	 * 
	 * @param tableDomain
	 *            表结构对象
	 * @param table
	 *            表标签信息
	 * @return 列对象集合
	 * @author dborm@cocho
	 * @time 2013-5-23下午3:27:02
	 */
	private Map<String, ColumnBean> getColumnDomains(TableBean tableDomain, Element table) {
		Map<String, ColumnBean> fieldList = tableDomain.getColumns();
		NodeList columnList = table.getElementsByTagName(SchemaConstants.COLUMN);
		for (int j = 0; j < columnList.getLength(); j++) {
			ColumnBean columnDomain = new ColumnBean();
			Element column = (Element) columnList.item(j);
			String fieldName = getStringMustAttributeValue(column, SchemaConstants.COLUMN_FIELD_NAME);
			columnDomain.setFieldName(fieldName);
			columnDomain.setPrimaryKey(getBooleanAttributeValue(column, SchemaConstants.COLUMN_IS_PRIMARY_KEY));
			columnDomain.setDefaultValue(getDefaultValue(column));
			String columnName = StringUtilsDborm.humpToUnderlineName(fieldName);
			fieldList.put(columnName, columnDomain);
		}
		return fieldList;
	}

	private Set<String> getRelations(TableBean tableDomain, Element table) {
		Set<String> relations = tableDomain.getRelation();
		NodeList relationList = table.getElementsByTagName(SchemaConstants.RELATION);
		for (int j = 0; j < relationList.getLength(); j++) {
			Element relation = (Element) relationList.item(j);
			String fieldName = getStringMustAttributeValue(relation, SchemaConstants.RELATION_FIELD_NAME);
			relations.add(fieldName);
		}
		return relations;
	}

	/**
	 * 获得必填属性的值
	 * 
	 * @param column
	 *            列标签信息
	 * @param attributeName
	 *            属性名称
	 * @return 属性值，如果没有值则抛出异常
	 * @author dborm@cocho
	 * @time 2013-5-24下午4:25:23
	 */
	private String getStringMustAttributeValue(Element column, String attributeName) {
		Node node = column.getAttributes().getNamedItem(attributeName);
		if (node != null) {
			return node.getNodeValue();
		} else {
			throw new IllegalArgumentException("属性(" + attributeName + ") 在配置文件( " + schemaPath + ")中不允许为空!");
		}
	}

	/**
	 * 获得字符串类型属性的值
	 * 
	 * @param column
	 *            列标签信息
	 * @param attributeName
	 *            属性名称
	 * @return 对应的值或null
	 * @author dborm@cocho
	 * @time 2013-5-24下午4:25:37
	 */
	private String getStringAttributeValue(Element column, String attributeName) {
		Node node = column.getAttributes().getNamedItem(attributeName);
		if (node != null) {
			return node.getNodeValue();
		} else {
			return "";
		}
	}

	/**
	 * 获得布尔类型的值
	 * 
	 * @param column
	 *            列标签信息
	 * @param attributeName
	 *            属性名称
	 * @return 如果有值则取值，默认为false
	 * @author dborm@cocho
	 * @time 2013-5-24下午4:26:13
	 */
	private boolean getBooleanAttributeValue(Element column, String attributeName) {
		Node node = column.getAttributes().getNamedItem(attributeName);
		return node != null && Boolean.parseBoolean(node.getNodeValue());
	}

	/**
	 * 获得设置的默认值
	 * 
	 * @param column
	 *            列标签信息
	 * @return 默认值，如果为设置则为null
	 * @author dborm@cocho
	 */
	private Object getDefaultValue(Element column) {
		Node node = column.getAttributes().getNamedItem(SchemaConstants.COLUMN_DEFAULT_VALUE);
		if (node != null) {
			return node.getNodeValue();
		}
		return null;
	}

    private boolean isValibleClassPath(String classPath){
        boolean result = true;
        try {
            Class.forName(classPath);
        }catch (Exception e){
            result = false;
            LoggerUtilsDborm.error("xml描述文件中描述的类[" + classPath + "]不存在!", e);
        }
        return result;
    }

	public String getSchemaPath() {
		return schemaPath;
	}

	public void setSchemaPath(String schemaPath) {
		this.schemaPath = schemaPath;
	}
}
